package org.langcom.logic.order;

import java.math.BigDecimal;
import java.util.Date;

import org.langcom.TimeUtils;
import org.langcom.logic.automata.AbstractAutomata;
import org.langcom.logic.automata.ActionUnavailableException;
import org.langcom.logic.automata.ActionWrapper;
import org.langcom.logic.automata.SkladManager;

public class OrderAutomataState extends AbstractAutomata<Order> {

	private Order order;
	private final Long accessorId;
	private final Long proposalIssuerId;
	private final boolean isAccessorAdmin;
	private final boolean isAccessorSystem;

	public OrderAutomataState(Order order) {
		this(order, null, false, true, null);
	}

	public OrderAutomataState(Order order, Long accessorId) {
		this(order, accessorId, SkladManager.isAdmin(accessorId), SkladManager.isSystem(accessorId), SkladManager.getProposalIssuerId(order.getId()));
	}

	public OrderAutomataState(Order order, Long accessorId, boolean isAccessorAdmin, boolean isAccessorSystem, Long proposalIssuerId) {
		super(order);
		this.accessorId = accessorId;
		this.isAccessorAdmin = isAccessorAdmin;
		this.isAccessorSystem = isAccessorSystem;
		this.proposalIssuerId = proposalIssuerId;
		process();
	}

	@Override
	public void process() {
		super.process();
		if (isAccessorSystem) {
			accessBySystem();
			return;
		}
		if (isAccessorAdmin) {
			accessByAdmin();
		} else {
			accessByParticipant();
		}
	}

	private void accessBySystem() {
		Date now = new Date();
		switch (order.getState()) {
			case NEW:
				if (TimeUtils.daysBetween(order.getCreated(), now) > 3) {
					setStateSystem(OrderState.ARCHIVE);
				}
				break;
			case INWORK:
				if (TimeUtils.daysBetween(order.getUpdated(), now) > 3) {
					setStateSystem(OrderState.ARCHIVE);
				}
				break;
			case CONFIRMED:
				if (TimeUtils.daysBetween(order.getUpdated(), now) > 3) {
					setStateSystem(OrderState.ARCHIVE);
				}
				break;
			case RESERVED:
				if (TimeUtils.daysBetween(order.getUpdated(), now) > 3) {
					setStateSystem(OrderState.ARCHIVE);
				}
				break;
			case PAID:
				if (TimeUtils.daysBetween(order.getUpdated(), now) > 7) {
					setStateSystem(OrderState.ARCHIVE);
				}
				break;
			case COMPLETED:
				if (TimeUtils.daysBetween(order.getUpdated(), now) > 7) {
					setStateSystem(OrderState.ARCHIVE);
				}
				break;
		}
	}

	private void accessByAdmin() {
		setAllPublic();
		setEvaluated("setQuantity");
		setEvaluated("setCurrencyId");
		setEvaluated("setMessage");
		setEvaluated("complete");
		setEvaluated("reserve");
		setEvaluated("paid");
		setEvaluated("toWork");
	}

	private void accessByParticipant() {
		switch (order.getState()) {
			case NEW:
				setAllPrivate();
				if (accessorId.equals(order.getPersonId())) {
					order.setIssuerConfirm(true);
					order.setContractorConfirm(false);
					setEvaluated("setQuantity");
					setEvaluated("setCurrencyId");
					setEvaluated("setMessage");
					setEvaluated("reject");
				} else if (accessorId.equals(proposalIssuerId)) {
					setEvaluated("toWork");
					setEvaluated("reject");
				}
				break;
			case INWORK:
				Concordance concordance = new Concordance();
				setAllPrivate();
				if (accessorId.equals(order.getPersonId())) {
					setEvaluated("setQuantity", concordance);
					setEvaluated("setCurrencyId", concordance);
					setEvaluated("setMessage");
					setEvaluated("setIssuerConfirm", new StateModification());
					setEvaluated("reject");
				} else if (accessorId.equals(proposalIssuerId)) {
					setEvaluated("setQuantity", concordance);
					setEvaluated("setCurrencyId", concordance);
					setEvaluated("setMessage");
					setEvaluated("setContractorConfirm", new StateModification());
					setEvaluated("reject");
				}
				break;
			case CONFIRMED:
				setAllPrivate();
				if (accessorId.equals(order.getPersonId())) {
					setEvaluated("setMessage");
					setEvaluated("reject");
				} else if (accessorId.equals(proposalIssuerId)) {
					setEvaluated("setMessage");
					setEvaluated("reserve");
					setEvaluated("reject");
				}
				break;
			case RESERVED:
				setAllPrivate();
				if (accessorId.equals(order.getPersonId())) {
					setEvaluated("setMessage");
					setEvaluated("reject");
				} else if (accessorId.equals(proposalIssuerId)) {
					setEvaluated("paid");
					setEvaluated("setMessage");
					setEvaluated("reject");
				}
				break;
			case PAID:
				setAllPrivate();
				if (accessorId.equals(order.getPersonId())) {
					setEvaluated("complete");
					setEvaluated("setMessage");
				} else if (accessorId.equals(proposalIssuerId)) {
					setEvaluated("setMessage");
				}
				break;
			case COMPLETED:
				setAllPrivate();
				if (accessorId.equals(order.getPersonId())) {
					setEvaluated("setMessage");
					setEvaluated("reject");
				} else if (accessorId.equals(proposalIssuerId)) {
					setEvaluated("setMessage");
					setEvaluated("reject");
				}
				break;
		}
	}

	private void setStateSystem(OrderState state) {
		order.setState(state);
		save();
	}

	private void setState(OrderState state) {
		order.setState(state);
		save();
		process();
	}

	private void save() {
		order.setUpdated(new Date());
		SkladManager.save(order);
	}

	public void reject() throws ActionUnavailableException {
		evaluate("reject");
		setState(OrderState.ARCHIVE);
	}

	public void toWork() throws ActionUnavailableException {
		evaluate("toWork");
		setState(OrderState.INWORK);
	}

	public void reserve() throws ActionUnavailableException {
		evaluate("reserve");
		setState(OrderState.RESERVED);
	}

	public void paid() throws ActionUnavailableException {
		evaluate("paid");
		setState(OrderState.PAID);
	}

	public void complete() throws ActionUnavailableException {
		evaluate("complete");
		setState(OrderState.COMPLETED);
	}

	public void setMessage(String message) throws ActionUnavailableException {
		evaluate("setMessage");
		executeBefore("setMessage");
		order.setMessage(message);
		executeAfter("setMessage");
		save();
	}

	public void setQuantity(BigDecimal quantity) throws ActionUnavailableException {
		evaluate("setQuantity");
		executeBefore("setQuantity");
		order.setQuantity(quantity);
		executeAfter("setQuantity");
		save();
	}

	public void setCurrencyId(Long currencyId) throws ActionUnavailableException {
		evaluate("setCurrencyId");
		executeBefore("setCurrencyId");
		order.setCurrencyId(currencyId);
		executeAfter("setCurrencyId");
		save();
	}

	public void setTotal(BigDecimal total) throws ActionUnavailableException {
		evaluate("setTotal");
		executeBefore("setTotal");
		order.setTotal(total);
		executeAfter("setTotal");
		save();
	}

	public void setDiscountId(Long discountId) throws ActionUnavailableException {
		evaluate("setDiscountId");
		executeBefore("setDiscountId");
		order.setDiscountId(discountId);
		executeAfter("setDiscountId");
	}

	public void setIssuerConfirm(Boolean issuerConfirm) throws ActionUnavailableException {
		evaluate("setIssuerConfirm");
		order.setIssuerConfirm(issuerConfirm);
		save();
	}

	public void setContractorConfirm(Boolean contractorConfirm) throws ActionUnavailableException {
		evaluate("setContractorConfirm");
		order.setContractorConfirm(contractorConfirm);
		save();
	}

	public String toString() {
		return order.toString();
	}

	public boolean equals(Object object) {
		return order.equals(object);
	}

	public Boolean getContractorConfirm() {
		return order.getContractorConfirm();
	}

	public Boolean getIssuerConfirm() {
		return order.getIssuerConfirm();
	}

	public Date getCreated() {
		return order.getCreated();
	}

	public Long getCurrencyId() {
		return order.getCurrencyId();
	}

	public Long getDiscountId() {
		return order.getDiscountId();
	}

	public Long getId() {
		return order.getId();
	}

	public String getMessage() {
		return order.getMessage();
	}

	public String getNumber() {
		return order.getNumber();
	}

	public Long getPersonId() {
		return order.getPersonId();
	}

	public Long getProposalId() {
		return order.getProposalId();
	}

	public BigDecimal getQuantity() throws ActionUnavailableException {
		return order.getQuantity();
	}

	public OrderState getState() {
		return order.getState();
	}

	public BigDecimal getTotal() throws ActionUnavailableException {
		return order.getTotal();
	}

	public Date getUpdated() {
		return order.getUpdated();
	}

	public int hashCode() {
		return order.hashCode();
	}
	private class Concordance implements ActionWrapper {

		@Override
		public void afterInvoke() {
			if (accessorId.equals(order.getPersonId())) {
				order.setContractorConfirm(false);
			} else if (accessorId.equals(proposalIssuerId)) {
				order.setIssuerConfirm(false);
			}
		}

		@Override
		public void beforeInvoke() {
		}
	}
	private class StateModification implements ActionWrapper {

		@Override
		public void afterInvoke() {
			if (order.getContractorConfirm() && order.getIssuerConfirm()) {
				setState(OrderState.CONFIRMED);
			}
		}

		@Override
		public void beforeInvoke() {
		}
	}
}
